package com.cc.data_explore.gui.tab;

import atlantafx.base.theme.Styles;
import com.cc.data_explore.gui.base.BaseView;
import com.cc.data_explore.gui.base.WFXGenericDialog;
import com.cc.data_explore.gui.helper.StageHelper;
import com.cc.data_explore.gui.importfile.ImportFileDialogView;
import com.cc.data_explore.gui.importfile.ImportFileDialogViewModel;
import com.cc.data_explore.gui.sheet.SheetDataDialogView;
import com.cc.data_explore.gui.sheet.SheetDataDialogViewModel;
import com.cc.data_explore.gui.tab.rule.NewConcatView;
import com.cc.data_explore.gui.tab.rule.NewConcatViewModel;
import com.cc.data_explore.gui.tab.rule.NewMappingFieldView;
import com.cc.data_explore.gui.tab.rule.NewMappingFieldViewModel;
import com.cc.data_explore.logic.data.TableRuleData;
import com.cc.data_explore.logic.mybatis.model.FileInfo;
import de.saxsys.mvvmfx.FluentViewLoader;
import de.saxsys.mvvmfx.FxmlView;
import de.saxsys.mvvmfx.InjectViewModel;
import de.saxsys.mvvmfx.ViewTuple;
import io.datafx.core.concurrent.ProcessChain;
import javafx.beans.InvalidationListener;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.WritableImage;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.split.SplitUtil;
import org.kordamp.ikonli.javafx.FontIcon;
import org.kordamp.ikonli.materialdesign2.MaterialDesignC;
import org.kordamp.ikonli.materialdesign2.MaterialDesignE;

import java.util.*;

/**
 * TabViewController
 *
 * @author aa
 * @version 1.0
 * @since 2025/3/27
 */
public class TabView extends BaseView implements FxmlView<TabViewModel> {

    @FXML
    public Button importDialogBtn;
    @FXML
    public TreeView<FileInfo> fileTreeView;
    @FXML
    public TableView<Map<String, String>> tableView;
    @FXML
    public FlowPane flowColList;
    @FXML
    public TableView<TableRuleData> ruleTableView;
    @FXML
    public TableColumn<TableRuleData, String> targetHeadColumn;
    @FXML
    public TableColumn<TableRuleData, String> sourceHeadColumn;
    @FXML
    public Button saveDataBtn;
    @FXML
    public Button viewImportData;
    @FXML
    public VBox vbox;

    @InjectViewModel
    private TabViewModel tabViewModel;

    private final ObservableMap<String, String> columnMap = FXCollections.observableMap(new LinkedHashMap<>());

    private final IntegerProperty useIndex = new SimpleIntegerProperty(-1);
    private final IntegerProperty headIndex = new SimpleIntegerProperty(0);
    private WFXGenericDialog dialog;

    @FXML
    public void initialize() {
        Bindings.bindContent(tableView.getItems(), tabViewModel.rowList());
        headIndex.bindBidirectional(tabViewModel.headIndexProperty());
        targetHeadColumn.setCellValueFactory(new PropertyValueFactory<>("targetName"));
        sourceHeadColumn.setCellValueFactory(new PropertyValueFactory<>("sourceName"));
        createDropTargetCell();
        Bindings.bindContentBidirectional(ruleTableView.getItems(), tabViewModel.headers());
        Bindings.bindContent(columnMap, tabViewModel.getColumnMap());
        fileTreeView.rootProperty().bind(tabViewModel.rootProperty());
        fileTreeView.setCellFactory(cell -> new TreeCell<>() {
            @Override
            protected void updateItem(FileInfo item, boolean empty) {
                super.updateItem(item, empty);
                if (empty || item == null) {
                    setGraphic(null);
                } else {
                    HBox hBox = new HBox();
                    Label label = new Label(item.getFileName());
                    hBox.getChildren().add(label);
                    if (getTreeItem().isLeaf()) {
                        Label viewBtn = new Label();
                        FontIcon icon1 = new FontIcon(MaterialDesignE.EYE_OUTLINE);
                        icon1.setIconSize(16);
                        viewBtn.setGraphic(icon1);
                        viewBtn.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                        viewBtn.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseEvent -> openViewDataDialog(item.getId()));
                        Label setPrimary = new Label();
                        FontIcon icon2 = new FontIcon(MaterialDesignC.COG_OUTLINE);
                        icon2.setIconSize(16);
                        setPrimary.setGraphic(icon2);
                        setPrimary.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                        setPrimary.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseEvent -> createPrimaryTable(item.getId()));
                        hBox.getChildren().add(viewBtn);
                        hBox.getChildren().add(setPrimary);
                    }
                    hBox.setSpacing(10);
                    hBox.setAlignment(Pos.TOP_LEFT);
                    setGraphic(hBox);
                    getGraphic().addEventHandler(MouseEvent.MOUSE_PRESSED, mouseEvent -> {
                        if (mouseEvent.getClickCount() >= 2 && getTreeItem().isLeaf() && !(mouseEvent.getTarget() instanceof FontIcon)) {
                            showData(item.getId());
                            mouseEvent.consume();
                        }
                    });
                }
            }
        });
        // 启用单元格选择模式
        ruleTableView.getSelectionModel().setCellSelectionEnabled(true);
        tableView.getSelectionModel().setCellSelectionEnabled(true);
        tableView.setRowFactory(call -> {
            TableRow<Map<String, String>> row = new TableRow<>();
            ContextMenu contextMenu = new ContextMenu();
            MenuItem headItem = new MenuItem("设置表头行");
            headItem.setOnAction(e -> {
                Map<String, String> data = row.getItem();
                columnMap.putAll(data);
                setCol();
                headIndex.set(row.getIndex());
            });
            contextMenu.getItems().addAll(headItem);
            // 绑定右键菜单到行
            row.contextMenuProperty().bind(Bindings.when(row.emptyProperty()).then((ContextMenu) null).otherwise(contextMenu));
            return row;
        });

        tableView.getSelectionModel().getSelectedCells().addListener((InvalidationListener) observable -> {
            ObservableList<TablePosition<Map<String, String>, String>> obs = (ObservableList<TablePosition<Map<String, String>, String>>) observable;
            obs.forEach(item -> useIndex.set(item.getColumn()));
        });

        saveDataBtn.setGraphic(FontIcon.of(MaterialDesignC.CONTENT_SAVE));
        saveDataBtn.getStyleClass().addAll(Styles.ACCENT, Styles.SMALL);
        viewImportData.setGraphic(FontIcon.of(MaterialDesignE.EYE_OUTLINE));
        viewImportData.getStyleClass().addAll(Styles.ACCENT, Styles.SMALL);
        importDialogBtn.getStyleClass().addAll(Styles.SMALL);
        tabViewModel.subscribe(TabViewModel.COLUMN_CHANGE, (key, payload) -> setCol());
        useIndex.addListener((observable, oldValue, newValue) -> {
            if (oldValue.equals(newValue)) {
                return;
            }
            int oldInt = oldValue.intValue();
            int newInt = newValue.intValue();
            if (CollUtil.isNotEmpty(flowColList.getChildren())) {
                if (flowColList.getChildren().size() > newInt) {
                    Node newNode = flowColList.getChildren().get(newInt);
                    Button newBtn = (Button) newNode;
                    newBtn.getStyleClass().addAll(Styles.ACCENT);
                }
                if (flowColList.getChildren().size() > oldInt && oldInt != -1) {
                    Node oldNode = flowColList.getChildren().get(oldInt);
                    Button oldBtn = (Button) oldNode;
                    oldBtn.getStyleClass().remove(Styles.ACCENT);
                }
            }
            TableColumn<Map<String, String>, ?> newColumn = tableView.getColumns().get(newInt);
            newColumn.getStyleClass().add(Styles.BG_ACCENT_MUTED);
            if (oldInt != -1) {
                TableColumn<Map<String, String>, ?> oldColumn = tableView.getColumns().get(oldInt);
                oldColumn.getStyleClass().remove(Styles.BG_ACCENT_MUTED);
            }
        });
    }

    private void setCol() {
        ObservableList<TableColumn<Map<String, String>, ?>> columns = tableView.getColumns();
        if (CollUtil.isEmpty(columns)) {
            columnMap.forEach((k, v) -> tableView.getColumns().add(this.buildColumn(k, v)));
        } else {
            columns.forEach((col) -> col.setText(columnMap.get(col.getId())));
        }
        setFlowColList();
    }

    private TableColumn<Map<String, String>, String> buildColumn(String k, String v) {
        TableColumn<Map<String, String>, String> column = new TableColumn<>(v);
        column.setId(k);
        column.setSortable(false);
        ContextMenu contextMenu = new ContextMenu();
        contextMenu.getItems().addAll(this.buildMenuItem());
        // 绑定右键菜单到行
        column.setContextMenu(contextMenu);
        column.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().get(k)));
        return column;
    }


    private List<MenuItem> buildMenuItem() {
        MenuItem concatItem = new MenuItem("新建串联字段");
        MenuItem mappingItem = new MenuItem("新建映射字段");
        concatItem.setOnAction(e -> showConcatRuleDialog());
        mappingItem.setOnAction(e -> showMappingRuleDialog());
        List<MenuItem> items = new ArrayList<>();
        items.add(concatItem);
        items.add(mappingItem);
        return items;
    }

    private void setFlowColList() {
        flowColList.getChildren().clear();
        columnMap.forEach((k, v) -> {
            if (StrUtil.isNotEmpty(v)) {
                Button titleBtn = new Button(v);
                titleBtn.getStyleClass().addAll(Styles.SMALL);
                titleBtn.setOnDragDetected(event -> {
                    Dragboard db = titleBtn.startDragAndDrop(TransferMode.COPY);
                    ClipboardContent content = new ClipboardContent();
                    // 设置拖拽时的图标
                    WritableImage writableImage = new WritableImage((int) titleBtn.getWidth(), (int) titleBtn.getHeight());
                    titleBtn.snapshot(new SnapshotParameters(), writableImage);
                    db.setDragView(writableImage, 10, 10);
                    content.putString(k + "," + v);
                    db.setContent(content);
                    // event.consume();
                });
                titleBtn.setOnDragDone(event -> titleBtn.setDisable(true));
                titleBtn.setOnAction(e -> {
                    int i = Integer.parseInt(k);
                    tableView.scrollToColumnIndex(i);
                    useIndex.set(i);
                });
                flowColList.getChildren().add(titleBtn);
            }
        });
    }

    private void createDropTargetCell() {
        sourceHeadColumn.setCellFactory(param -> {
            TableCell<TableRuleData, String> cell = new TableCell<>() {
                @Override
                protected void updateItem(String item, boolean empty) {
                    super.updateItem(item, empty);
                    setText(empty ? null : item);
                }
            };
            ContextMenu contextMenu = new ContextMenu();
            MenuItem cleanData = getMenuItem(cell);
            contextMenu.getItems().add(cleanData);
            cell.setContextMenu(contextMenu);
            // 设置拖拽进入时的样式
            cell.setOnDragEntered(e -> cell.getStyleClass().add(Styles.BG_ACCENT_MUTED));
            // 设置拖拽离开时的样式
            cell.setOnDragExited(e -> cell.getStyleClass().remove(Styles.BG_ACCENT_MUTED));
            // 设置拖拽放置处理
            cell.setOnDragOver(e -> {
                if (e.getDragboard().hasString()) {
                    e.acceptTransferModes(TransferMode.COPY);
                }
                e.consume();
            });

            // 处理拖拽释放
            cell.setOnDragDropped(e -> {
                Dragboard db = e.getDragboard();
                String content = db.getString();
                boolean success = false;
                if (StrUtil.isNotEmpty(content)) {
                    List<String> contents = SplitUtil.splitTrim(content, ",");
                    TableRuleData item = cell.getTableRow().getItem();
                    if (item != null) {
                        item.setSourceIndex(Integer.parseInt(contents.getFirst()));
                        item.setSourceName(contents.getLast());
                        success = true;
                    }
                }
                e.setDropCompleted(success);
                e.consume();
            });
            return cell;
        });
    }

    private MenuItem getMenuItem(TableCell<TableRuleData, String> cell) {
        MenuItem cleanMenu = new MenuItem("清空");
        cleanMenu.setOnAction(e -> {
            TableRuleData item = cell.getTableRow().getItem();
            int sourceIndex = item.getSourceIndex();
            if (sourceIndex == -1) {
                return;
            }
            item.setSourceIndex(-1);
            item.setSourceName(null);
            Node node = flowColList.getChildren().get(sourceIndex);
            Button btn = (Button) node;
            btn.setDisable(false);
        });
        return cleanMenu;
    }

    public void openImportDialog(ActionEvent actionEvent) {
        ViewTuple<ImportFileDialogView, ImportFileDialogViewModel> load = FluentViewLoader.fxmlView(ImportFileDialogView.class).load();
        showDialog("导入文件", load.getView(), false);
    }

    public void openViewDataDialog(Long sheetId) {
        ViewTuple<SheetDataDialogView, SheetDataDialogViewModel> load = FluentViewLoader.fxmlView(SheetDataDialogView.class).load();
        SheetDataDialogViewModel viewModel = load.getViewModel();
        viewModel.initData(sheetId);
        showDialog("浏览数据", load.getView());
    }

    private void createPrimaryTable(Long sheetId) {
        boolean checked = tabViewModel.checkPrimaryTable();
        if (checked) {
            Alert alert = new Alert(Alert.AlertType.ERROR);
            alert.setContentText("该数据源已存在分析表");
            alert.initOwner(StageHelper.getStage(StageHelper.MAIN_STAGE));
            alert.showAndWait();
            return;
        }
        ViewTuple<CreateTableDialogView, CreateTableDialogViewModel> load = FluentViewLoader.fxmlView(CreateTableDialogView.class).load();
        load.getViewModel().initData(sheetId);
        showDialog("创建分析表", load.getView());
    }

    public void showData(Long sheetId) {
        Boolean checked = tabViewModel.checkIsSelected(sheetId);
        if (checked == null) {
            tabViewModel.loadData();
            return;
        }
        if (!checked) {
            Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
            alert.setTitle("提示");
            alert.setHeaderText("编辑新sheet表");
            alert.setContentText("请确认，将清空当前已修改的数据");
            Optional<ButtonType> result = alert.showAndWait();
            if (result.isPresent() && result.get() == ButtonType.OK) {
                tabViewModel.loadData();
            }
        }
    }

    public WFXGenericDialog getDialogContent() {
        if (dialog == null) {
            dialog = new WFXGenericDialog();
        }
        return dialog;
    }

    public void viewImportData(ActionEvent actionEvent) {
        ViewTuple<CatImportDataDialogView, CatImportDataDialogViewModel> load = FluentViewLoader.fxmlView(CatImportDataDialogView.class).load();
        load.getViewModel().loadData();
        showDialog("浏览已导入数据", load.getView());
    }


    public void showConcatRuleDialog() {
        Map<String, String> previewData = tableView.getItems().get(headIndex.getValue() + 1);
        ObservableList<TreeItem<FileInfo>> selectedItems = fileTreeView.getSelectionModel().getSelectedItems();
        TreeItem<FileInfo> first = selectedItems.getFirst();
        String fileName = first.getValue().getFileName();
        ViewTuple<NewConcatView, NewConcatViewModel> load = FluentViewLoader.fxmlView(NewConcatView.class).load();

        load.getViewModel().initData(columnMap, fileName, previewData);
        getDialogContent().clearActions();
        getDialogContent().addActions(Map.entry(new Button("取消"), event -> dialog.close()), Map.entry(new Button("确定"), event -> {
            ProcessChain.create().addSupplierInExecutor(() -> load.getViewModel().save()).addConsumerInPlatformThread(r -> {
                if (r) {
                    dialog.close();
                }
            }).onException(Throwable::printStackTrace).run();
        }));
        getDialogContent().setHeaderIcon(FontIcon.of(MaterialDesignE.EYE_OUTLINE));
        getDialogContent().setHeaderText("新建串联规则");
        getDialogContent().setContent(load.getView());
        getDialogContent().show(vbox.getScene());
    }

    public void showMappingRuleDialog() {
        ViewTuple<NewMappingFieldView, NewMappingFieldViewModel> load = FluentViewLoader.fxmlView(NewMappingFieldView.class).load();
        getDialogContent().clearActions();
        getDialogContent().addActions(Map.entry(new Button("取消"), event -> dialog.close()), Map.entry(new Button("确定"), event -> {
            ProcessChain.create().addSupplierInExecutor(() -> load.getViewModel().save()).addConsumerInPlatformThread(r -> {
                if (r) {
                    dialog.close();
                }
            }).onException(Throwable::printStackTrace).run();
        }));
        getDialogContent().setHeaderIcon(FontIcon.of(MaterialDesignE.EYE_OUTLINE));
        getDialogContent().setHeaderText("新建映射规则");
        getDialogContent().setContent(load.getView());
        getDialogContent().show(vbox.getScene());
    }

    public void saveDataBtnFun(ActionEvent actionEvent) {
        tabViewModel.saveCleanData();
    }
}
